# -*- coding: utf8 -*-
import os
import re
import json
import configparser
import threading
from datetime import datetime
from urllib import parse
from urllib.parse import quote

import requests


class HeathReport(object):
    
    def __init__(self, user):
        """
        :params username: 手机号或学号
        :params password: 密码
        :params schoolid: 学校代码，学号登录填写
        """
        headers = {
            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest',
        }
        self._username = user['username']
        self._password = user['password']
        self._schoolid = user['schoolid']
        self._session = requests.session()
        self._session.headers = headers
    
    def _login(self):
        """
        登录: 支持手机和邮箱登录
        """
        login_api = "https://passport2.chaoxing.com/api/login"
        params = {
            "name": self._username,
            "pwd": self._password,
            "verify": "0",
            "schoolid": self._schoolid if self._schoolid else ""
        }
        resp = self._session.get(login_api, params=params)
        
        if resp.status_code == 403:
            raise Exception("403，登录请求被拒绝")
        
        data = json.loads(resp.text)
        if data['result'] is False:
            raise Exception(data['errorMsg'])
        
        return data
    
    def _get_last_heath_info(self) -> dict:
        """
        获取上次提交的健康信息
        """
        params = {
            "cpage": "1",
            "formId": "7185",
            "enc": "f837c93e0de9d9ad82db707b2c27241e",
            "formAppId": ""
        }
        api = 'http://office.chaoxing.com/data/apps/forms/fore/forms/user/last/info'
        resp = self._session.get(api, params=params)
        raw_data = json.loads(resp.text)
        if not raw_data['data']:
            raise Exception('获取上次提交数据为空，可能为今日已提交')
        return raw_data
    
    def _read_form_data_file(self):
        with open('./report_template.json', 'r', encoding='utf-8') as f:
            return f.read()
    
    def _set_value(self, last_report_data, form_data_template):
        def get_val(data: dict, _id: str):
            return data['data']['formsUser']['formIdValueData'][_id]['groupValues'][0]['values'][0][0]
        
        username = get_val(last_report_data, '1')['val']
        id_number = get_val(last_report_data, '2')['val']
        telephone_number = get_val(last_report_data, '3')['val']
        address = get_val(last_report_data, '4')
        
        form_data = form_data_template.replace("$cx_username", username). \
            replace("$cx_id_number", id_number). \
            replace("$cx_telephone_number", telephone_number). \
            replace("$cx_address", address['address']). \
            replace("$cx_lng", address['lng']). \
            replace("$cx_lat", address['lat'])
        return form_data
    
    @staticmethod
    def form_data_to_urlencoded(params: dict, form_data: str) -> str:
        """
        dict -> urlencoded
        """
        payload = parse.urlencode(params)
        payload += "&formData=" + quote(form_data, 'utf-8')
        payload = payload.replace("%2B", "+")
        return payload
    
    def _daily_report(self, check_code: str, form_data: str) -> dict:
        """
        上报今日信息
        """
        save_api = "http://office.chaoxing.com/data/apps/forms/fore/user/save?lookuid=127973604"
        params = {
            "gatherId": "0",
            "formId": "7185",
            "formAppId": "",
            "version": 6,
            "checkCode": check_code,
            "enc": "f837c93e0de9d9ad82db707b2c27241e",
            "anonymous": 0,
            "ext": "",
            "t": 1,
            "uniqueCondition": [],
            "gverify": ""
        }
        payload = self.form_data_to_urlencoded(params, form_data)
        resp = self._session.post(save_api, data=payload)
        return json.loads(resp.text)
    
    def _request_form_page(self):
        """
        请求表单页面
        @return:
        @rtype:
        """
        form_url = "http://office.chaoxing.com/front/web/apps/forms/fore/apply?uid=127973604&code=l5RJsW2w&mappId=4545821&appId=1e354ddb52a743e88ed19a3704b1cf1a&appKey=127G2jhIhl05mw3S&id=7185&enc=f837c93e0de9d9ad82db707b2c27241e&state=39037&formAppId=&fidEnc=b06cba4a51ac2253"
        return self._session.get(url=form_url)
    
    def _get_check_code(self):
        """
        解析表单界面获取checkCode
        @return: checkCode
        @rtype: str
        """
        resp = self._request_form_page()
        code = re.findall(r"checkCode.*'(.*)'", resp.text)
        if code:
            return code[0]
        else:
            raise Exception("校验码获取失败")
    
    def daily_report(self) -> dict:
        """
        健康信息上报入口
        """
        self._login()
        last_report_data = self._get_last_heath_info()
        form_data_template = self._read_form_data_file()
        form_data = self._set_value(last_report_data, form_data_template)
        check_code = self._get_check_code()
        return self._daily_report(check_code=check_code, form_data=form_data)


def start_report(user):
    result = HeathReport(user).daily_report()
    print(f"{user['username']} - 打卡结果: \n {result}")
    try:
        sendkey = user['sendkey']
    except Exception as exc:
        sendkey = None
    
    if not sendkey:
        print(f"{user['username']} - 未开启消息推送")
        return
    
    resp = server_chan_send(result, sendkey)
    try:
        resp.raise_for_status()
        print(f"{user['username']} - 本次打卡详情已发送")
    except Exception as exc:
        text = exc.response.json()
        print(f"{user['username']} - 消息发送失败，原因：{text['info']}")


def server_chan_send(msg, key):
    """server酱将消息推送"""
    params = {
        'title': '健康日报打卡消息来啦！\n{}'.format(datetime.now().strftime('%Y年%m月%d日 %H:%M:%D')),
        'desp': msg
    }
    
    resp = requests.request(
        method="GET",
        url=f"https://sctapi.ftqq.com/{key}.send?title=messagetitle",
        params=params
    )
    return resp


def load_user_config():
    """
    加载每个用户的配置
    """
    config = configparser.ConfigParser()
    users = []
    
    if os.getenv("cx_env") == "dev":
        # 加载开发环境配置文件
        config.read('./config.dev.ini')
    else:
        config.read('./config.ini')
    
    for sections in config:
        section = config[sections]
        if 'user' not in str(section):
            continue
        else:
            try:
                open_status = section['open']
            except Exception as exc:
                raise Exception("open字段必填，true 或者 false") from exc
            
            if open_status in ("true", "True"):
                users.append(section)
    
    username = os.environ.get('username')
    if username:
        users.append({
            'username': os.environ.get('username'),
            'password': os.environ.get('password'),
            'schoolid': os.environ.get('schoolid'),
            'sendkey': os.environ.get('schoolid')
        })
    
    if not users:
        raise Exception("当前暂无账号执行，请在config.ini 或 环境变量中配置账号密码")
    
    return users


def main_handler(event=None, context=None):
    if event is not None:
        query: dict = event.get("queryString", "")
        if query:
            
            user = dict(
                username=query.get("name", None),
                password=query.get("pwd", None),
                schoolid=query.get("schoolid", ""),
                send_key=query.get("skey", None),
            )
            
            try:
                h = HeathReport(user)
                result = h.daily_report()
            except Exception as e:
                result = e
            return result
    else:
        threads = []
        for user in load_user_config():
            t = threading.Thread(target=start_report, args=(user,))
            t.start()
            threads.append(t)
        
        for t in threads:
            t.join()


if __name__ == '__main__':
    main_handler()
